//自己实现一个定时器  用到的数据结构：带有阻塞功能的优先级队列
// 定时器就是同时管理多个任务然后定时，让先到时间的任务下执行，然后让第二到时间的任务再执行，依次类推
/*1.此时还需要注意两个问题：优先级队列传过去的参数时一个类，要写比较规则
  2.while这循环是无条件循环的 ，如果没到时间就会一直循环，占用CPU的资源，要加一个wait，等到时间了，再进行唤醒，避免CPU资源的浪费*/
import java.util.Timer;
import java.util.concurrent.PriorityBlockingQueue;
class MyTask1 implements Comparable<MyTask1>{//这个类就表示了一个类
    public Runnable runnable;//这个任务干啥
    public long time;//这个任务啥时候执行
    //方便后续判断时间 使用的是绝对时间戳     绝对时间戳 ：就是那一串代表ms的数字
    public MyTask1(Runnable runnable,long delay) {
        this.runnable = runnable;
        this.time = System.currentTimeMillis() + delay;
    }

    @Override
    public int compareTo(MyTask1 o) {
        return(int)(this.time - o.time);
    }
}
class MyTimer1 {
    private Object locker = new Object();
    private PriorityBlockingQueue<MyTask1> queue = new PriorityBlockingQueue<>();
    //泛型的参数就是一个任务，所以封装成一个类来描述这个任务：1.这个任务要干啥 2.这个任务啥时候执行
    public void schedule(Runnable runnable,long delay) {
        //根据当调用schedule方法时的参数来构造myTask类
        MyTask1 myTask1 = new MyTask1(runnable, delay);
        queue.put(myTask1);
        synchronized (locker) {
            /*这个唤醒操作不是到时间了去唤醒，wait那里写了一个最大等待的时间了，
            这个唤醒是当新加入进来一个新的任务时，比如 2：30 上课，但是但是2：10我想起来要去接水，
            所以两点时我看了一下上课是2：30，我要等30分钟，但是2：10加入了一个接水的任务，这个时候就暂时不去等
            两点半上课的任务了，去等这个新加进来的任务，所以在2：10（不到上课这个任务的时间呢），就唤醒了，去wait
            等新加入进来的任务*/
            locker.notify();
        }
    }
    public MyTimer1() {
        Thread t = new Thread(() -> {
            while (true) {
                try {
                    synchronized (locker) {
                        MyTask1 myTask1 = queue.take();
                        long currentTime = System.currentTimeMillis();
                        if (myTask1.time <= currentTime) {
                            //到时间了  就执行任务
                            myTask1.runnable.run();
                        }else {
                            //不到时间就把这个任务放回队列中
                            queue.put(myTask1);
                            locker.wait(myTask1.time - currentTime);
                        }
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        t.start();
    }
}
public class ThreadDemo27Practice {
    public static void main(String[] args) {
        MyTimer1 myTimer1 = new MyTimer1();
        myTimer1.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("hello4");
            }
        },4000);
        myTimer1.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("hello3");
            }
        },3000);
        myTimer1.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("hello2");
            }
        },2000);
        myTimer1.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("hello1");
            }
        },1000);
        System.out.println("hello 00");
    }
}
